Imports System
Imports System.Text
Imports System.Data
Imports System.Collections.Generic

Imports Framework

#Region "Enum: Primary Key Values"
'<CLSCompliant(True)> _
'Public Enum EPayment
'    Huey = 1
'    Duey = 2
'    Louie = 3
'End Enum
#End Region

'Table-Row Class (Customisable half)
Partial Public Class CPayment

#Region "Constants"
    'Join Expressions
    Public Shared JOIN_ORDER As String = VIEW_NAME 'String.Concat(TABLE_NAME, " INNER JOIN ", COrder.TABLE_NAME, " ON PaymentOrderId=OrderId") 'Nullable => Use LEFT OUTER JOIN
#End Region

#Region "Constructors (Public)"
    'Default Connection String
    Public Sub New()
        MyBase.New()
    End Sub
    Public Sub New([paymentId] As Integer)
        MyBase.New([paymentId])
    End Sub

    'Alternative Connection String
    Public Sub New(ByVal dataSrc As CDataSrc)
        MyBase.New(dataSrc)
    End Sub
    Public Sub New(ByVal dataSrc As CDataSrc, [paymentId] As Integer)
        MyBase.New(dataSrc, [paymentId])
    End Sub

    'Transactional (shares an open connection)
    Protected Friend Sub New(ByVal dataSrc As CDataSrc, ByVal [paymentId] As Integer, ByVal txOrNull As IDbTransaction)
        MyBase.New(dataSrc, [paymentId], txOrNull)
    End Sub
#End Region

#Region "Default Values"
    Protected Overrides Sub InitValues_Custom()
        'Custom default values (e.g. DateCreated column)
        m_paymentCreated = DateTime.Now
        'Member variables (e.g. for child collections)

    End Sub
#End Region

#Region "Default Connection String"
    Protected Overrides Function DefaultDataSrc() As CDataSrc
        Return CDataSrc.Default
    End Function
#End Region

#Region "Members"
    'Foreign Keys   
    <NonSerialized()> Private m_order As COrder

    'Child Collections  

    'Xml Data (as high-level objects)

#End Region

#Region "Properties - Relationships"
    'Relationships - Foriegn Keys (e.g parent)
    Public Property [Order]() As COrder
        Get
            If IsNothing(m_order) Then
                SyncLock (Me)
                    If IsNothing(m_order) Then
                        If Integer.MinValue = Me.PaymentOrderId Then
                            m_order = New COrder
                        Else
                            m_order = New COrder(Me.PaymentOrderId)
                        End If
                    End If
                End SyncLock
            End If
            Return m_order
        End Get
        Set(Value As COrder)
            m_order = Value
            If Not IsNothing(Value) Then m_PaymentOrderId = Value.OrderId Else m_PaymentOrderId = Integer.MinValue
        End Set
    End Property

    'Relationships - Collections (e.g. children)

#End Region

#Region "Properties - Customisation"
    Private m_details As Dictionary(Of String, String)
    Private m_detailsString As String
    Public ReadOnly Property Details() As Dictionary(Of String, String)
        Get
            If IsNothing(m_details) Then
                m_details = Split(PaymentDetails)
            End If
            Return m_details
        End Get
    End Property
    Public ReadOnly Property DetailsString() As String
        Get
            If IsNothing(m_detailsString) Then
                SyncLock (Me)
                    If IsNothing(m_detailsString) Then
                        Dim sb As New StringBuilder()
                        Dim keys As New List(Of String)(Details.Keys)
                        keys.Sort()
                        For Each i As String In keys
                            sb.Append(i).Append(vbTab).Append("=").Append(vbTab).AppendLine(Details(i))
                        Next
                        m_detailsString = sb.ToString
                    End If
                End SyncLock
            End If
            Return m_detailsString
        End Get
    End Property

    Public ReadOnly Property Country As String
        Get
            Dim s As String = String.Empty
            Details.TryGetValue("residence_country", s)
            Return s
        End Get
    End Property


    Public ReadOnly Property PaymentNet As Decimal
        Get
            If Decimal.MinValue = PaymentFee Then Return PaymentAmount
            Return PaymentAmount - PaymentFee
        End Get
    End Property
#End Region

#Region "Save/Delete Overrides"
    'Can Override MyBase.Save/Delete (e.g. Cascade deletes, or insert related records)
#End Region

#Region "Custom Database Queries"
    Public Function SelectByOrderIds(ByVal orderIds As List(Of Integer)) As CPaymentList
        Return SelectWhere(New CCriteriaList("PaymentOrderId", ESign.IN, orderIds))
    End Function

    Public Function SelectCountByProduct(ByVal productId As Integer) As Integer
        Return SelectCount(New CCriteriaList("OrderProductId", productId))
    End Function
    Public Function SelectSumOkByProduct(ByVal productId As Integer) As Integer
        Dim where As New CCriteriaList(2)
        where.Add("OrderProductId", productId)
        where.Add("PaymentIsOk", True)
        Return SelectSum("PaymentAmount", Nothing, where)
    End Function
    Public Function SelectCountByCustomer(ByVal customerId As Integer) As Integer
        Return SelectCount(New CCriteriaList("OrderCustomerId", customerId))
    End Function
#End Region

#Region "Searching (Optional)"
    'Dynamic search methods: (overload as required for common search patterns, cascade the BuildWhere overloads)
    '   Public  x5 - Simple, Paged, Transactional, Count, and Dataset
    '   Private x1 - BuildWhere
    'See also in-memory search options in list class, such as GetBy[FK] and Search

    'Simple
    Public Function SelectSearch(ByVal nameOrId As String, ByVal orderId As Integer, customerId As Integer, productId As Integer) As CPaymentList
        Return SelectWhere(BuildWhere(nameOrId, orderId, customerId, productId), JOIN_ORDER)
    End Function

    'Paged
    Public Function SelectSearch(ByVal pi As CPagingInfo, ByVal nameOrId As String, ByVal orderId As Integer, customerId As Integer, productId As Integer) As CPaymentList
        pi.TableName = JOIN_ORDER
        Return SelectWhere(pi, BuildWhere(nameOrId, orderId, customerId, productId))
    End Function

    'Transactional
    Public Function SelectSearch(ByVal nameOrId As String, ByVal orderId As Integer, customerId As Integer, productId As Integer, ByVal tx As IDbTransaction) As CPaymentList
        Return SelectWhere(BuildWhere(nameOrId, orderId, customerId, productId), JOIN_ORDER, tx)
    End Function

    'Count
    Public Overloads Function SelectCount(ByVal nameOrId As String, ByVal orderId As Integer, customerId As Integer, productId As Integer) As Integer
        Return SelectCount(BuildWhere(nameOrId, orderId, customerId, productId), JOIN_ORDER)
    End Function

    'Dataset (e.g. ExportToCsv)
    Public Function SelectSearch_Dataset(ByVal nameOrId As String, ByVal orderId As Integer, customerId As Integer, productId As Integer) As DataSet
        Return SelectWhere_Dataset(BuildWhere(nameOrId, orderId, customerId, productId), JOIN_ORDER)
    End Function


    'Filter Logic
    'Represents a simple search box to search PK and any string columns
    Private Shared Function BuildWhere(ByVal nameOrId As String) As CCriteriaList
        Dim where As New CCriteriaList 'Defaults to AND logic

        'Simple search box UI
        If Not String.IsNullOrEmpty(nameOrId) Then
            'Interpret search string in various ways using OR sub-expression
            Dim orExpr As New CCriteriaGroup(EBoolOperator.Or)

            'Special case - search by PK (assumes integer PK)
            Dim id As Integer
            If Integer.TryParse(nameOrId, id) Then
                orExpr.Add("PaymentId", id)
            End If

            'Search a range of string columns
            Dim wildCards As String = String.Concat("%", nameOrId, "%")
            orExpr.Add("PaymentTransaction", ESign.Like, wildCards)
            orExpr.Add("CustomerName", ESign.Like, wildCards)
            orExpr.Add("CustomerEmail", ESign.Like, wildCards)
            orExpr.Add("ProductName", ESign.Like, wildCards)
            orExpr.Add("PaymentItemName", ESign.Like, wildCards)
            orExpr.Add("PaymentFirstName", ESign.Like, wildCards)
            orExpr.Add("PaymentLastName", ESign.Like, wildCards)
            orExpr.Add("PaymentMemo", ESign.Like, wildCards)

            'Conclude
            If orExpr.Group.Count > 0 Then
                where.Add(orExpr)
            End If
        End If

        Return where
    End Function
    'Represents more complex combinations of search filters (suggestion only)
    Public Shared Function BuildWhere(ByVal nameOrId As String, ByVal orderId As Integer, customerId As Integer, productId As Integer) As CCriteriaList
        Dim where As CCriteriaList = BuildWhere(nameOrId) 'Reuses logic above

        'Other search Colums (customise as required)
        If Integer.MinValue <> orderId Then where.Add("PaymentOrderId", orderId)
        If Integer.MinValue <> customerId Then where.Add("OrderCustomerId", customerId)
        If Integer.MinValue <> productId Then where.Add("OrderProductId", productId)

        Return where
    End Function
#End Region

#Region "Cloning"
    Public Function Clone(target As CDataSrc, txOrNull As IDbTransaction) As CPayment ', parentId As Integer) As CPayment
        'Shallow copy: Copies the immediate record, excluding autogenerated Pks
        Dim copy As New CPayment(Me, target)

        'Deep Copy - Child Entities: Cloned children must reference their cloned parent
        'copy.SampleParentId = parentId

        copy.Save(txOrNull)

        'Deep Copy - Parent Entities: Cloned parents also clone their child collections
        'Me.Children.Clone(target, txOrNull, copy.PaymentId)

        Return copy
    End Function
#End Region

#Region "ToXml"
    Protected Overrides Sub ToXml_Custom(ByVal w As System.Xml.XmlWriter)
        'Store(w, "Example", Me.Example)
    End Sub
#End Region

    Public Shared Function Split(ByVal s As String) As Dictionary(Of String, String)
        Dim pairs As String() = s.Split("&")
        Dim dict As New Dictionary(Of String, String)(pairs.Length)
        For Each i As String In pairs
            Dim ss As String() = i.Split("=")
            If ss.Length > 1 Then
                Try
                    dict(ss(0)) = System.Web.HttpContext.Current.Server.UrlDecode(ss(1))
                Catch
                    dict(ss(0)) = ss(1)
                End Try
            ElseIf ss(0).Length > 0 Then
                dict(ss(0)) = String.Empty
            End If
        Next
        Return dict
    End Function

    Public Class CTotals
        Public Total As Decimal
        Public Fee As Decimal

        Public ReadOnly Property Net As Decimal
            Get
                If Decimal.MinValue = Total Then Return Decimal.MinValue
                If Decimal.MinValue = Fee Then Return Decimal.MinValue
                Return Total - Fee
            End Get
        End Property

        Private Shared SELECT_COLS As String = "SUM(PaymentAmount), SUM(PaymentFee)"

        Public Sub New(search As String, customerId As Integer, productId As Integer, orderId As Integer)
            Dim ds As DataSet = Nothing
            If String.IsNullOrEmpty(search) Then
                ds = CDataSrc.Default.SelectAll_Dataset(SELECT_COLS, VIEW_NAME, Nothing)
            Else
                ds = CDataSrc.Default.SelectWhere_Dataset(SELECT_COLS, VIEW_NAME, Nothing, CPayment.BuildWhere(search, orderId, customerId, productId))
            End If
            Dim dr As DataRow = ds.Tables(0).Rows(0)

            Total = CAdoData.GetDec(dr, 0)
            Fee = CAdoData.GetDec(dr, 1)
        End Sub
    End Class

End Class
